﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SCMS.Model;
using System.Transactions;
using System.Data.Objects.DataClasses;
using SCMS.CoreBusinessLogic.NotificationsManager;

namespace SCMS.CoreBusinessLogic.TravelAuthorisation
{
    public class TravelAuthService : ITravelAuthService
    {
        private INotificationService notificationService;

        private _ExchangeRate.IExchangeRateService exchangeRateService;

        public TravelAuthService(_ExchangeRate.IExchangeRateService exchangeRateService, INotificationService NotificationService)
        {
            this.exchangeRateService = exchangeRateService;
            this.notificationService = NotificationService;
        }

        public bool SaveTravalAuth(Model.TravelAuthorization entity, Model.TravelDate TADateEntity)
        {
            using (var db = new SCMSEntities())
            {
                using (TransactionScope scope = new TransactionScope())
                {
                    try
                    {
                        TADateEntity.Id = Guid.NewGuid();
                        var taf = (entity != null) ? entity : db.TravelAuthorizations.FirstOrDefault(p => p.Id == TADateEntity.TravelAuthorizationId);
                        decimal newTotalAdvnceRequired = exchangeRateService.GetForeignCurrencyValue((Guid)taf.CurrencyId, (Guid)TADateEntity.CurrencyId, (decimal)TADateEntity.AdvanceRequired, (Guid)taf.CountryProgrammeId);
                        taf.TotalAmount += newTotalAdvnceRequired;

                        if (entity != null) db.TravelAuthorizations.AddObject(taf);
                        db.TravelDates.AddObject(TADateEntity);
                        if (entity == null) db.ObjectStateManager.ChangeObjectState(taf, System.Data.EntityState.Modified);
                        if (!(db.SaveChanges() > 0))
                        { scope.Dispose(); return false; }
                        scope.Complete();
                        return true;
                    }
                    catch (Exception ex) { scope.Dispose(); throw new Exception(ex.Message); }
                }
            }
        }

        public bool SaveTravalAuth(Model.TravelAuthorization entity, Model.TravelExpense TAExpenseEntity)
        {
            using (var db = new SCMSEntities())
            {
                using (TransactionScope scope = new TransactionScope())
                {
                    try
                    {
                        TAExpenseEntity.Id = Guid.NewGuid();
                        var taf = (entity != null) ? entity : db.TravelAuthorizations.FirstOrDefault(p => p.Id == TAExpenseEntity.TravelAuthorizationId);
                        decimal newTotalAdvnceRequired = exchangeRateService.GetForeignCurrencyValue((Guid)taf.CurrencyId, (Guid)TAExpenseEntity.CurrencyId, (decimal)TAExpenseEntity.AdvanceRequired, (Guid)taf.CountryProgrammeId);
                        taf.TotalAmount += newTotalAdvnceRequired;

                        if (entity != null) db.TravelAuthorizations.AddObject(taf);
                        db.TravelExpenses.AddObject(TAExpenseEntity);
                        if (entity == null) db.ObjectStateManager.ChangeObjectState(taf, System.Data.EntityState.Modified);
                        if (!(db.SaveChanges() > 0))
                        { scope.Dispose(); return false; }
                        scope.Complete();
                        return true;
                    }
                    catch (Exception ex) { scope.Dispose(); throw new Exception(ex.Message); }
                }
            }
        }

        public bool SubmitTAF(Model.TravelAuthorization entity)
        {
            using (var dbContext = new SCMSEntities())
            {
                dbContext.TravelAuthorizations.Attach(entity);
                dbContext.ObjectStateManager.ChangeObjectState(entity, System.Data.EntityState.Modified);
                if (dbContext.SaveChanges() > 0)
                { notificationService.SendToAppropriateApprover(NotificationHelper.tafCode, NotificationHelper.reviewCode, entity.Id); return true; }
                else return false;
            }
        }

        public List<Model.TravelAuthorization> GetTAuthForms(Guid cpId)
        {
            using (var db = new SCMSEntities())
            {
                List<Model.TravelAuthorization> tauthlist = db.TravelAuthorizations.Where(p => p.CountryProgrammeId == cpId).OrderByDescending(t => t.RefNumber).ToList();
                foreach (var item in tauthlist)
                {
                    Model.Currency c = item.Currency;
                    EntityCollection<TravelDate> td = item.TravelDates;
                    EntityCollection<TravelExpense> te = item.TravelExpenses;
                }
                return tauthlist;
            }
        }

        public string GenerateUniquNumber(CountryProgramme cp)
        {
            string code = "TAF/DRC/" + cp.Country.ShortName + "/";
            string refNumber = "";
            long count = 1;
            using (var dbContext = new SCMSEntities())
            {
                var total = dbContext.TravelAuthorizations.Where(p => p.CountryProgrammeId == cp.Id && p.IsSubmitted == true).Count();
                count = total;
                Model.TravelAuthorization m = null;
                do
                {
                    count++;
                    if (count < 10000)
                    {
                        if (count < 10)
                            refNumber = code + "0000" + count;
                        if (count < 100 & count >= 10)
                            refNumber = code + "000" + count;
                        if (count < 1000 & count >= 100)
                            refNumber = code + "00" + count;
                        if (count < 10000 & count >= 1000)
                            refNumber = code + "0" + count;
                    }
                    m = dbContext.TravelAuthorizations.FirstOrDefault(p => p.RefNumber == refNumber);
                } while (m != null);
                return refNumber;
            }
        }

        public Model.TravelAuthorization GetTAFById(Guid TAFId)
        {
            using (var db = new SCMSEntities())
            {
                var taf = db.TravelAuthorizations.FirstOrDefault(p => p.Id == TAFId);
                if (taf.Staff != null)
                {
                    var person = taf.Staff.Person;
                    var desg = taf.Staff.Designation;
                }
                if (taf.Staff1 != null)
                {
                    var person = taf.Staff1.Person;
                    var desg = taf.Staff1.Designation;
                }
                if (taf.Staff2 != null)
                {
                    var person = taf.Staff2.Person;
                    var desg = taf.Staff2.Designation;
                }
                if (taf.Staff3 != null)
                {
                    var person = taf.Staff3.Person;
                    var desg = taf.Staff3.Designation;
                }
                if (taf.Staff4 != null)
                {
                    var person = taf.Staff4.Person;
                    var desg = taf.Staff4.Designation;
                }
                return taf;
            }
        }

        public Model.TravelDate GetTAFDateById(Guid TafDateId)
        {
            using (var context = new SCMSEntities())
            {
                return context.TravelDates.FirstOrDefault(t => t.Id == TafDateId);
            }
        }

        public Model.TravelExpense GetTAFExpenseById(Guid TafExpenseId)
        {
            using (var context = new SCMSEntities())
            {
                return context.TravelExpenses.FirstOrDefault(t => t.Id == TafExpenseId);
            }
        }

        public List<TravelDate> GetTAFDates(Guid tafId)
        {
            using (var context = new SCMSEntities())
            {
                var tafDates = context.TravelDates.Where(t => t.TravelAuthorizationId == tafId).ToList();
                foreach (var item in tafDates)
                {
                    Model.Currency c = item.Currency;
                    Model.ProjectBudget pb = item.ProjectBudget;
                    Model.BudgetCategory bc = pb.BudgetCategory;
                    Model.ProjectDonor pd = bc.ProjectDonor;
                }
                return tafDates;
            }
        }

        public List<TravelExpense> GetTAFExpenses(Guid tafId)
        {
            using (var context = new SCMSEntities())
            {
                var tafExpenses = context.TravelExpenses.Where(t => t.TravelAuthorizationId == tafId).ToList();
                foreach (var item in tafExpenses)
                {
                    Model.Currency c = item.Currency;
                    Model.ProjectBudget pb = item.ProjectBudget;
                    Model.BudgetCategory bc = pb.BudgetCategory;
                    Model.ProjectDonor pd = bc.ProjectDonor;
                }
                return tafExpenses;
            }
        }
        
        public List<Model.TravelAuthorization> GetTravelAuthorizationsForReview(SystemUser currentUser)
        {
            try
            {
                using (var context = new SCMSEntities())
                {
                    List<Model.TravelAuthorization> travelAuthorizations = new List<Model.TravelAuthorization>();
                    context.SystemUsers.Attach(currentUser);
                    var approvers = context.Approvers.Where(a => a.UserId == currentUser.Id && a.ActivityCode == NotificationHelper.tafCode && a.ActionType == NotificationHelper.reviewCode).ToList();
                    var delegateApprovers = context.Approvers.Where(a => a.AssistantId == currentUser.Id && a.ActivityCode == NotificationHelper.tafCode && a.ActionType == NotificationHelper.reviewCode).ToList();
                    if (approvers != null)
                    {
                        foreach (var approver in approvers)
                        {
                            var tafList = context.TravelAuthorizations.Where(e => e.CountryProgrammeId == currentUser.Staff.CountrySubOffice.CountryProgrammeId && (e.IsSubmitted == true && e.IsReviewed == false && e.IsRejected == false)
                                && e.Notifications.Where(n => (Guid)n.TravelAuthorizationId == e.Id && n.IsRespondedTo == false && n.SentToDelegate == false && n.ApproverId == approver.Id).Count() > 0).ToList();
                            foreach (var taf in tafList)
                            {
                                travelAuthorizations.Add(taf);
                            }
                        }
                    }
                    if (delegateApprovers != null)
                    {
                        foreach (var delegateApprover in delegateApprovers)
                        {
                            var tafList = context.TravelAuthorizations.Where(e => e.CountryProgrammeId == currentUser.Staff.CountrySubOffice.CountryProgrammeId && (e.IsSubmitted == true && e.IsReviewed == false && e.IsRejected == false)
                                && e.Notifications.Where(n => (Guid)n.TravelAuthorizationId == e.Id && n.IsRespondedTo == false && n.SentToDelegate == true && n.ApproverId == delegateApprover.Id).Count() > 0).ToList();
                            foreach (var taf in tafList)
                            {
                                travelAuthorizations.Add(taf);
                            }
                        }
                    }
                    return travelAuthorizations;
                }
            }
            catch (Exception ex)
            {
                return new List<Model.TravelAuthorization>();
            }
        }

        public List<Model.TravelAuthorization> GetTravelAuthorizationsForAuth(SystemUser currentUser)
        {
            try
            {
                using (var context = new SCMSEntities())
                {
                    List<Model.TravelAuthorization> travelAuthorizations = new List<Model.TravelAuthorization>();
                    context.SystemUsers.Attach(currentUser);
                    var approvers = context.Approvers.Where(a => a.UserId == currentUser.Id && a.ActivityCode == NotificationHelper.tafCode && a.ActionType == NotificationHelper.authorizationCode).ToList();
                    var delegateApprovers = context.Approvers.Where(a => a.AssistantId == currentUser.Id && a.ActivityCode == NotificationHelper.tafCode && a.ActionType == NotificationHelper.authorizationCode).ToList();
                    if (approvers != null)
                    {
                        foreach (var approver in approvers)
                        {
                            var tafList = context.TravelAuthorizations.Where(e => e.CountryProgrammeId == currentUser.Staff.CountrySubOffice.CountryProgrammeId && (e.IsReviewed == true && e.IsAuthorized == false && e.IsRejected == false)
                                && e.Notifications.Where(n => (Guid)n.TravelAuthorizationId == e.Id && n.IsRespondedTo == false && n.SentToDelegate == false && n.ApproverId == approver.Id).Count() > 0).ToList();
                            foreach (var taf in tafList)
                            {
                                travelAuthorizations.Add(taf);
                            }
                        }
                    }
                    if (delegateApprovers != null)
                    {
                        foreach (var delegateApprover in delegateApprovers)
                        {
                            var tafList = context.TravelAuthorizations.Where(e => e.CountryProgrammeId == currentUser.Staff.CountrySubOffice.CountryProgrammeId && (e.IsReviewed == true && e.IsAuthorized == false && e.IsRejected == false)
                                && e.Notifications.Where(n => (Guid)n.TravelAuthorizationId == e.Id && n.IsRespondedTo == false && n.SentToDelegate == true && n.ApproverId == delegateApprover.Id).Count() > 0).ToList();
                            foreach (var taf in tafList)
                            {
                                travelAuthorizations.Add(taf);
                            }
                        }
                    }
                    return travelAuthorizations;
                }
            }
            catch (Exception ex)
            {
                return new List<Model.TravelAuthorization>();
            }
        }

        public List<TravelAuthorization> GetTravelAuthorizationsForPosting(Guid countryProgId, SystemUser currentUser)
        {
            using (var context = new SCMSEntities())
            {
                List<TravelAuthorization> travelAuthorizations = new List<TravelAuthorization>();
                var approvers = context.Approvers.Where(a => a.CountryProgrammeId == countryProgId && a.ActivityCode == NotificationHelper.tafCode && a.ActionType == NotificationHelper.postFundsCode
                    && (a.UserId == currentUser.Id || a.AssistantId == currentUser.Id));
                var financeLimit = context.SystemUsers.FirstOrDefault(s => s.Id == currentUser.Id).Staff.FinanceLimit;
                if (financeLimit == null)
                    return new List<TravelAuthorization>();
                if (approvers.ToList().Count == 0)
                    return new List<TravelAuthorization>();
                if (approvers.Where(a => a.ProjectDonorId == null).Count() > 0)
                {
                    travelAuthorizations = context.TravelAuthorizations.Where(t => t.CountryProgrammeId == countryProgId && t.IsAuthorized == true && t.FundsPosted == false
                        && (t.MBValue.Value <= financeLimit.Limit || financeLimit.Limit == 0)).ToList();
                    foreach (var taf in travelAuthorizations)
                    {
                        if (taf.Staff != null)
                        {
                            var person = taf.Staff.Person;
                            var desg = taf.Staff.Designation;
                        }
                        if (taf.Staff1 != null)
                        {
                            var person = taf.Staff1.Person;
                            var desg = taf.Staff1.Designation;
                        }
                        if (taf.Staff2 != null)
                        {
                            var person = taf.Staff2.Person;
                            var desg = taf.Staff2.Designation;
                        }
                        if (taf.Staff3 != null)
                        {
                            var person = taf.Staff3.Person;
                            var desg = taf.Staff3.Designation;
                        }
                        if (taf.Staff4 != null)
                        {
                            var person = taf.Staff4.Person;
                            var desg = taf.Staff4.Designation;
                        }
                    }
                    return travelAuthorizations;
                }
                
                foreach (var approver in approvers.ToList())
                {
                    var tafs = context.TravelAuthorizations.Where(t => t.CountryProgrammeId == countryProgId && t.IsAuthorized == true && t.FundsPosted == false
                        && (t.MBValue.Value <= financeLimit.Limit || financeLimit.Limit == 0)
                        && (t.TravelDates.Where(i => i.ProjectBudget.BudgetCategory.ProjectDonorId.Value == approver.ProjectDonorId.Value).Count() > 0)
                            || t.TravelExpenses.Where(i => i.ProjectBudget.BudgetCategory.ProjectDonorId.Value == approver.ProjectDonorId.Value).Count() > 0).ToList();
                    foreach (var taf in tafs)
                    {
                        if (!TafExistsInList(taf, travelAuthorizations))
                        {
                            if (taf.Staff != null)
                            {
                                var person = taf.Staff.Person;
                                var desg = taf.Staff.Designation;
                            }
                            if (taf.Staff1 != null)
                            {
                                var person = taf.Staff1.Person;
                                var desg = taf.Staff1.Designation;
                            }
                            if (taf.Staff2 != null)
                            {
                                var person = taf.Staff2.Person;
                                var desg = taf.Staff2.Designation;
                            }
                            if (taf.Staff3 != null)
                            {
                                var person = taf.Staff3.Person;
                                var desg = taf.Staff3.Designation;
                            }
                            if (taf.Staff4 != null)
                            {
                                var person = taf.Staff4.Person;
                                var desg = taf.Staff4.Designation;
                            }
                            travelAuthorizations.Add(taf);
                        }
                    }
                }
                return travelAuthorizations;
            }
        }

        private bool TafExistsInList(TravelAuthorization taf, List<TravelAuthorization> tafList)
        {
            foreach (var e in tafList)
            {
                if (e.Equals(taf))
                    return true;
            }
            return false;
        }

        public void SaveApprovedTAF(TravelAuthorization taf)
        {
            using (var context = new SCMSEntities())
            {
                context.TravelAuthorizations.Attach(taf);
                context.ObjectStateManager.ChangeObjectState(taf, System.Data.EntityState.Modified);
                context.SaveChanges();
            }
        }

        public void SaveApprovedTAFDate(TravelDate tafDate)
        {
            using (var context = new SCMSEntities())
            {
                context.TravelDates.Attach(tafDate);
                context.ObjectStateManager.ChangeObjectState(tafDate, System.Data.EntityState.Modified);
                context.SaveChanges();
            }
        }

        public void SaveApprovedTAFExpense(TravelExpense tafExpense)
        {
            using (var context = new SCMSEntities())
            {
                context.TravelExpenses.Attach(tafExpense);
                context.ObjectStateManager.ChangeObjectState(tafExpense, System.Data.EntityState.Modified);
                context.SaveChanges();
            }
        }

        public bool CommitFunds(TravelAuthorization taf)
        {
            decimal commitAmount = 0;
            using (var context = SCMSEntities.Define())
            {
                using (TransactionScope scope = new TransactionScope())
                {
                    try
                    {
                        var tafDates = context.TravelDates.Where(d => d.TravelAuthorizationId == taf.Id).ToList();
                        var tafExpenses = context.TravelExpenses.Where(d => d.TravelAuthorizationId == taf.Id).ToList();
                        foreach (var tafDate in tafDates)
                        {
                            commitAmount = (decimal)exchangeRateService.GetForeignCurrencyValue(tafDate.ProjectBudget.BudgetCategory.ProjectDonor.Currency, tafDate.Currency, tafDate.TotalAllowance, tafDate.TravelAuthorization.CountryProgrammeId.Value);
                            var bc = new BudgetCommitment();
                            bc.Id = Guid.NewGuid();
                            bc.AmountCommitted = commitAmount;
                            bc.BudgetLineId = tafDate.ProjectBudget.Id;
                            bc.DateCommitted = DateTime.Now;
                            bc.TravelDateId = tafDate.Id;
                            context.BudgetCommitments.AddObject(bc);
                            tafDate.ProjectBudget.TotalCommitted += commitAmount;
                        }

                        foreach (var tafExpense in tafExpenses)
                        {
                            commitAmount = (decimal)exchangeRateService.GetForeignCurrencyValue(tafExpense.ProjectBudget.BudgetCategory.ProjectDonor.Currency, tafExpense.Currency, tafExpense.TotalCost, tafExpense.TravelAuthorization.CountryProgrammeId.Value);
                            var bc = new BudgetCommitment();
                            bc.Id = Guid.NewGuid();
                            bc.AmountCommitted = commitAmount;
                            bc.BudgetLineId = tafExpense.ProjectBudget.Id;
                            bc.DateCommitted = DateTime.Now;
                            bc.TravelExpenseId = tafExpense.Id;
                            context.BudgetCommitments.AddObject(bc);
                            tafExpense.ProjectBudget.TotalCommitted += commitAmount;
                        }

                        if (context.SaveChanges() > 0)
                        {
                            scope.Complete();
                            return true;
                        }
                        else
                        {
                            scope.Dispose();
                            return false;
                        }
                    }
                    catch (Exception ex)
                    {
                        scope.Dispose();
                        throw ex;
                    }
                }
            }
        }

        public bool EffectPosting(TravelAuthorization taf, Staff poster)
        {
            using (var context = SCMSEntities.Define())
            {
                using (TransactionScope scope = new TransactionScope())
                {
                    try
                    {
                        var tafDateCommits = context.BudgetCommitments.Where(b => b.TravelDate.TravelAuthorizationId == taf.Id).ToList();
                        foreach (var tafDateCommit in tafDateCommits)
                        {
                            var pb = tafDateCommit.ProjectBudget;
                            pb.TotalCommitted -= tafDateCommit.AmountCommitted;

                            var bp = new BudgetPosting();
                            bp.Id = Guid.NewGuid();
                            bp.AmountPosted = tafDateCommit.AmountCommitted;
                            bp.DatePosted = DateTime.Now;
                            bp.PostedBy = poster.Id;
                            bp.TravelDateId = tafDateCommit.TravelDateId;
                            pb.TotalPosted += bp.AmountPosted;

                            //Delete commitment and add posting
                            context.BudgetCommitments.DeleteObject(tafDateCommit);
                            context.BudgetPostings.AddObject(bp);
                        }

                        var tafExpenseCommits = context.BudgetCommitments.Where(b => b.TravelExpense.TravelAuthorizationId == taf.Id).ToList();
                        foreach (var tafExpenseCommit in tafExpenseCommits)
                        {
                            var pb = tafExpenseCommit.ProjectBudget;
                            pb.TotalCommitted -= tafExpenseCommit.AmountCommitted;

                            var bp = new BudgetPosting();
                            bp.Id = Guid.NewGuid();
                            bp.AmountPosted = tafExpenseCommit.AmountCommitted;
                            bp.DatePosted = DateTime.Now;
                            bp.PostedBy = poster.Id;
                            bp.TravelExpenseId = tafExpenseCommit.TravelExpenseId;
                            pb.TotalPosted += bp.AmountPosted;

                            //Delete commitment and add posting
                            context.BudgetCommitments.DeleteObject(tafExpenseCommit);
                            context.BudgetPostings.AddObject(bp);
                        }

                        var tafEntity = context.TravelAuthorizations.FirstOrDefault(a => a.Id == taf.Id);
                        tafEntity.FundsPosted = true;
                        tafEntity.PostedBy = poster.Id;
                        tafEntity.PostedOn = DateTime.Now;

                        if (context.SaveChanges() > 0)
                        {
                            scope.Complete();
                            return true;
                        }
                        else
                        {
                            scope.Dispose();
                            return false;
                        }
                    }
                    catch (Exception ex)
                    {
                        scope.Dispose();
                        throw ex;
                    }
                }
            }
        }

        public void NotifyAffected(TravelAuthorization taf)
        {
            using (var context = new SCMSEntities())
            {
                //Get ProjectBLCount list order by highest budget line count to least for this RFA
                List<ProjectBLCount> projects = context.TravelDates.Where(t => t.TravelAuthorizationId == taf.Id).GroupBy(bl => bl.ProjectBudget.BudgetCategory.ProjectDonor).
                    Select(projList => new ProjectBLCount
                    {
                        ProjectDonorId = projList.FirstOrDefault().ProjectBudget.BudgetCategory.ProjectDonor.Id,
                        BudgetLineCount = projList.Count()
                    }).OrderByDescending(p => p.BudgetLineCount).ToList<ProjectBLCount>();

                //Add travel dates project budget lines to projects list
                var travelExpenseProjects = taf.TravelExpenses.GroupBy(bl => bl.ProjectBudget.BudgetCategory.ProjectDonor).
                    Select(projList => new ProjectBLCount
                    {
                        ProjectDonorId = projList.FirstOrDefault().ProjectBudget.BudgetCategory.ProjectDonor.Id,
                        BudgetLineCount = projList.Count()
                    }).OrderByDescending(p => p.BudgetLineCount).ToList<ProjectBLCount>();
                foreach (var trvlExp in travelExpenseProjects)
                {
                    if (!NotificationHelper.ProjectExistsInList(trvlExp, projects))
                        projects.Add(trvlExp);
                }

                //Notify requestor
                var requestor = context.Staffs.FirstOrDefault(s => s.Id == taf.PreparedBy).Person;
                var msgBody = string.Format(NotificationHelper.tafFundsPostedMsgBody, requestor.FirstName, taf.RefNumber);
                notificationService.SendNotification(requestor.OfficialEmail, msgBody, NotificationHelper.tafsubject);

                //Notify the Project Manager(s)
                foreach (var project in projects)
                {
                    var proj = context.ProjectDonors.FirstOrDefault(p => p.Id == project.ProjectDonorId);
                    var name = proj.Staff.Person.FirstName;
                    var email = proj.Staff.Person.OfficialEmail;
                    msgBody = string.Format(NotificationHelper.tafPMNotifyFundsPostedMsgBody, name, taf.RefNumber, proj.ProjectNumber);
                    notificationService.SendNotification(email, msgBody, NotificationHelper.tafsubject);
                }
            }
        }
    }
}
